/**
 *
 * This part of the code come from the script.aculo.us wiki:
 * http://wiki.script.aculo.us/scriptaculous/show/ExtendClassFurther
 * This function is loaded after prototype to overload Class.create
 */
 
/**
 * @namespace Archetype.Class
 * @param {Object} proto
 */
Archetype.Class={
    // Creators
    /**
     * Creates an object from his prototype definition
     * @param {HashMap} proto
     */
    create: function (proto)
    {
        /* Accepting prototype definition of the class.  Idea presented by Greg
         * Wiley: http://wiki.script.aculo.us/scriptaculous/show/ExtendClass
         */
        var klass = function ()
        {

            if (typeof this.initialize == "function" && this.initialize && arguments.callee.caller != Archetype.Class.extend)
            {
                this.__class__ = arguments.callee.prototype;
                Object.extend(this, Archetype.Class.Methods);
                this.initialize.apply(this, arguments);
            }
        };
        klass.prototype = proto || {};
        klass.extend = Archetype.Class.extend;
        //Add name information to the method 
		for( var m in klass.prototype) {
			if($exist(klass.prototype[m]))
				klass.prototype[m].name = m; 
		}
        return klass;
    },
    /**
     * Creates a singleton getter (you can get the instance by using the [Name Of The Class].instance().
     * Used like Class.create(prototype) but to ensure a singleton
     * @param {HashMap} proto
     */
    singleton: function (proto)
    {
        var klass =
        {
            instance: function ()
            {
                if (!this.__instance__)
                {
                	/** @ignore */
                    var klass = Archetype.Class.create(proto);
                    /** @ignore */
                    this.__instance__ = eval('new klass');
                    proto = null;
                }
                return this.__instance__;
            }
        };
        return klass;
    },

    // Care-takers
    append_features: function (object, module)
    {
        for (var prop in module)
		{
            if (Archetype.Class.kind_of(module[prop], Function))
                (function (method)
                {
                    object[method] = function ()
                    {
                        return module[method].apply(object, arguments);
                    };
                })(prop);
            else
                object[prop] = module[prop];
		}
    },
    extend: function (subobj)   // implementation of inheritance
    {
        /* Create an instance of the superclass.  This is necessary to maintain
         * a prototype chain correctly.
         */
        var subproto = new this;
        // Update it with the subclass definitions
        Object.extend(subproto, subobj);
        subproto.__super__ = this.prototype;
        return Archetype.Class.create(subproto);
    },
    get_method: function (klass, args)  // obtain method name being called
    {
        var c = args.callee.caller;
        for (var method in klass)
            if (klass[method] == c)
                return method;
        return null;
    },
    call_super: function (superclass, self, method, args)
    {
        // NOTE: `self' should always refer to the object being instantiated
        if (superclass && superclass[method])
        {
            /* Make the next call of `this.SUPER()' in the superclass refer
             * farther up the class hierarchy */
            var __class__  = self.__class__;
            self.__class__ = superclass;
            self.__super__ = superclass.__super__;

            try
            {
                superclass[method].apply(self, args);
            }
            finally
            {
                // Restore values
                self.__class__ = __class__;
                self.__super__ = superclass;
            }
        }
    },
    kind_of: function (object, klass)
    {
        return eval('klass.prototype.isPrototypeOf(object)');
    }
};

/**
 * @namespace Archetype.Class.Methods
 * Mixed into class-instances, so that these methods will become available as
 * instance methods
 */
Archetype.Class.Methods =
{
    extend: function ()
    {
        var i = arguments.length;
        while (--i >= 0)
            Object.extend(this, arguments[i]);
        return this;
    },
    include: function ()
    {
        var i = arguments.length;
        while (--i >= 0)
            Archetype.Class.append_features(this, arguments[i]);
        return this;
    },
    kind_of: function (klass)
    {
        return Archetype.Class.kind_of(this, klass);
    },
    SUPER: function ()
    {
        var method = Archetype.Class.get_method(this.__class__, arguments);
        Archetype.Class.call_super(this.__super__, this, method, arguments);
    }
};
